/*-------------------------------------------------------------------------
 *
 * execSRF.c
 *	  实现集返回函数API的例程
 *
 * 本文件服务于nodeFunctionscan.c和nodeProjectSet.c，提供
 * 根据ReturnSetInfo API调用集返回函数的公共代码。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/execSRF.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "executor/execdebug.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"


/* 静态函数声明 */
static void fc_init_sexpr(Oid fc_foid, Oid fc_input_collation, Expr *fc_node,
					   SetExprState *fc_sexpr, PlanState *fc_parent,
					   MemoryContext fc_sexprCxt, bool fc_allowSRF, bool fc_needDescForSRF);
static void fc_ShutdownSetExpr(Datum fc_arg);
static void fc_ExecEvalFuncArgs(FunctionCallInfo fcinfo,
							 List *fc_argList, ExprContext *fc_econtext);
static void fc_ExecPrepareTuplestoreResult(SetExprState *fc_sexpr,
										ExprContext *fc_econtext,
										Tuplestorestate *fc_resultStore,
										TupleDesc fc_resultDesc);
static void fc_tupledesc_match(TupleDesc fc_dst_tupdesc, TupleDesc fc_src_tupdesc);


/*
 * 准备从 (ROWS FROM) 中执行的函数调用。
 *
 * 这是由 nodeFunctionscan.c 使用。
 */
SetExprState *
ExecInitTableFunctionResult(Expr *fc_expr,
							ExprContext *fc_econtext, PlanState *fc_parent)
{
	SetExprState *fc_state = makeNode(SetExprState);

	fc_state->funcReturnsSet = false;
	fc_state->expr = fc_expr;
	fc_state->func.fn_oid = InvalidOid;

	/*
	 * 通常，传递的表达式树将是 FuncExpr，因为文法只允许在表
	 * 函数引用的顶层进行函数调用。然而，如果函数不返回集合，则
	 * 规划器可能已通过常量折叠或内联替代了函数调用。因此，如果我们
	 * 看到任何其他类型的表达式节点，通过通用的 ExecEvalExpr()
	 * 代码执行它。然而，该代码路径不支持在表达式中嵌套的集合返回函数。
	 */
	if (IsA(fc_expr, FuncExpr))
	{
		FuncExpr   *fc_func = (FuncExpr *) fc_expr;

		fc_state->funcReturnsSet = fc_func->funcretset;
		fc_state->args = ExecInitExprList(fc_func->args, fc_parent);

		fc_init_sexpr(fc_func->funcid, fc_func->inputcollid, fc_expr, fc_state, fc_parent,
				   fc_econtext->ecxt_per_query_memory, fc_func->funcretset, false);
	}
	else
	{
		fc_state->elidedFuncState = ExecInitExpr(fc_expr, fc_parent);
	}

	return fc_state;
}

/*
 *		ExecMakeTableFunctionResult
 *
 * 评估一个表函数，在 Tuplestore 对象中产生一个物化的结果。
 *
 * 这是由 nodeFunctionscan.c 使用。
 */
Tuplestorestate *
ExecMakeTableFunctionResult(SetExprState *fc_setexpr,
							ExprContext *fc_econtext,
							MemoryContext fc_argContext,
							TupleDesc fc_expectedDesc,
							bool fc_randomAccess)
{
	Tuplestorestate *fc_tupstore = NULL;
	TupleDesc	fc_tupdesc = NULL;
	Oid			fc_funcrettype;
	bool		fc_returnsTuple;
	bool		fc_returnsSet = false;
	FunctionCallInfo fcinfo;
	PgStat_FunctionCallUsage fc_fcusage;
	ReturnSetInfo fc_rsinfo;
	HeapTupleData fc_tmptup;
	MemoryContext fc_callerContext;
	bool		fc_first_time = true;

	/*
	 * 在适当的上下文中执行每个表函数的操作。
	 *
	 * FunctionCallInfo 需要在 ValuePerCall 函数的所有调用之间
	 * 存在，因此它不能分配在每元组上下文中。同样，函数参数需要
	 * 在比每元组上下文长时间存在的上下文中评估：否则当我们在
	 * 内循环中重置该上下文时，参数值会消失。由于调用者的
	 * CurrentMemoryContext 通常是一个查询生命周期上下文，我们不想在
	 * 这里泄漏内存。我们要求调用者传递一个可以用于此的单独内存上下文，
	 * 并且可以在每次循环时重置以避免膨胀。
	 */
	MemoryContextReset(fc_argContext);
	fc_callerContext = MemoryContextSwitchTo(fc_argContext);

	fc_funcrettype = exprType((Node *) fc_setexpr->expr);

	fc_returnsTuple = type_is_rowtype(fc_funcrettype);

	/*
	 * 准备一个结果信息节点用于通信。即使不期望集合结果，我们也
	 * 始终这样做，以便我们可以传递 expectedDesc。在通用表达式
	 * 的情况下，表达式实际上并不看到 resultinfo，但仍然设置它，
	 * 因为我们将某些字段用作我们自己的状态变量。
	 */
	fc_rsinfo.type = T_ReturnSetInfo;
	fc_rsinfo.econtext = fc_econtext;
	fc_rsinfo.expectedDesc = fc_expectedDesc;
	fc_rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
	if (fc_randomAccess)
		fc_rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
	fc_rsinfo.returnMode = SFRM_ValuePerCall;
	/* isDone 在下面填充 */
	fc_rsinfo.setResult = NULL;
	fc_rsinfo.setDesc = NULL;

	fcinfo = palloc(SizeForFunctionCallInfo(list_length(fc_setexpr->args)));

	/*
	 * 通常，传递的表达式树将是 SetExprState，因为文法只允许在表
	 * 函数引用的顶层进行函数调用。然而，如果函数不返回集合，则
	 * 规划器可能已通过常量折叠或内联替代了函数调用。因此，如果我们
	 * 看到任何其他类型的表达式节点，通过通用的 ExecEvalExpr()
	 * 代码执行它；唯一的区别是我们没有机会向嵌入在表达式中的任何
	 * 函数传递特殊的 ReturnSetInfo。
	 */
	if (!fc_setexpr->elidedFuncState)
	{
		/*
		 * 该路径类似于 ExecMakeFunctionResultSet。
		 */
		fc_returnsSet = fc_setexpr->funcReturnsSet;
		InitFunctionCallInfoData(*fcinfo, &(fc_setexpr->func),
								 list_length(fc_setexpr->args),
								 fc_setexpr->fcinfo->fncollation,
								 NULL, (Node *) &fc_rsinfo);
		/* 评估函数的参数列表 */
		Assert(CurrentMemoryContext == fc_argContext);
		fc_ExecEvalFuncArgs(fcinfo, fc_setexpr->args, fc_econtext);

		/*
		 * 如果函数是严格的，并且有任何 NULL 参数，跳过调用函数，
		 * 并表现得像是返回 NULL（或在返回集合的情况下返回空集合）。
		 */
		if (fc_setexpr->func.fn_strict)
		{
			int			fc_i;

			for (fc_i = 0; fc_i < fcinfo->nargs; fc_i++)
			{
				if (fcinfo->args[fc_i].isnull)
					goto no_function_result;
			}
		}
	}
	else
	{
		/* 将集合表达式视为通用表达式 */
		InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
	}

	/*
	 * 切换到短期上下文以调用函数或表达式。
	 */
	MemoryContextSwitchTo(fc_econtext->ecxt_per_tuple_memory);

	/*
	 * 循环以处理 ValuePerCall 协议（这也是在通用 ExecEvalExpr
	 * 路径中需要的相同行为）。
	 */
	for (;;)
	{
		Datum		fc_result;

		CHECK_FOR_INTERRUPTS();

		/*
		 * 在每次调用函数或表达式之前重置每元组内存上下文。这将
		 * 清理函数调用时可能泄漏的任何本地内存。
		 */
		ResetExprContext(fc_econtext);

		/* 调用一次函数或表达式 */
		if (!fc_setexpr->elidedFuncState)
		{
			pgstat_init_function_usage(fcinfo, &fc_fcusage);

			fcinfo->isnull = false;
			fc_rsinfo.isDone = ExprSingleResult;
			fc_result = FunctionCallInvoke(fcinfo);

			pgstat_end_function_usage(&fc_fcusage,
									  fc_rsinfo.isDone != ExprMultipleResult);
		}
		else
		{
			fc_result =
				ExecEvalExpr(fc_setexpr->elidedFuncState, fc_econtext, &fcinfo->isnull);
			fc_rsinfo.isDone = ExprSingleResult;
		}

		/* 函数想要使用哪个协议？ */
		if (fc_rsinfo.returnMode == SFRM_ValuePerCall)
		{
			/*
			 * 检查结果集的结束。
			 */
			if (fc_rsinfo.isDone == ExprEndResult)
				break;

			/*
			 * 如果是第一次，通过构建 tuplestore 来存储结果。对于标量
			 * 函数结果类型，也创建适合的 tupdesc。
			 */
			if (fc_first_time)
			{
				MemoryContext fc_oldcontext =
				MemoryContextSwitchTo(fc_econtext->ecxt_per_query_memory);

				fc_tupstore = tuplestore_begin_heap(fc_randomAccess, false, work_mem);
				fc_rsinfo.setResult = fc_tupstore;
				if (!fc_returnsTuple)
				{
					fc_tupdesc = CreateTemplateTupleDesc(1);
					TupleDescInitEntry(fc_tupdesc,
									   (AttrNumber) 1,
									   "column",
									   fc_funcrettype,
									   -1,
									   0);
					fc_rsinfo.setDesc = fc_tupdesc;
				}
				MemoryContextSwitchTo(fc_oldcontext);
			}

			/*
			 * 存储当前结果集项。
			 */
			if (fc_returnsTuple)
			{
				if (!fcinfo->isnull)
				{
					HeapTupleHeader fc_td = DatumGetHeapTupleHeader(fc_result);

					if (fc_tupdesc == NULL)
					{
						MemoryContext fc_oldcontext =
						MemoryContextSwitchTo(fc_econtext->ecxt_per_query_memory);

						/*
						 * 这是函数返回的第一个非 NULL 结果。使用
						 * 嵌入在行类型 Datum 中的类型信息查找所需的 tupdesc。
						 * 为查询制作一个副本。
						 */
						fc_tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(fc_td),
															  HeapTupleHeaderGetTypMod(fc_td));
						fc_rsinfo.setDesc = fc_tupdesc;
						MemoryContextSwitchTo(fc_oldcontext);
					}
					else
					{
						/*
						 * 验证所有后续返回的行具有相同的子类型；
						 * 如果类型是 RECORD，则这是必要的。
						 */
						if (HeapTupleHeaderGetTypeId(fc_td) != fc_tupdesc->tdtypeid ||
							HeapTupleHeaderGetTypMod(fc_td) != fc_tupdesc->tdtypmod)
							ereport(ERROR,
									(errcode(ERRCODE_DATATYPE_MISMATCH),
									 errmsg("rows returned by function are not all of the same row type")));
					}

					/*
					 * tuplestore_puttuple 需要 HeapTuple 而不是裸
					 * HeapTupleHeader，但它不需要所有字段。
					 */
					fc_tmptup.t_len = HeapTupleHeaderGetDatumLength(fc_td);
					fc_tmptup.t_data = fc_td;

					tuplestore_puttuple(fc_tupstore, &fc_tmptup);
				}
				else
				{
					/*
					 * 从返回元组的函数得到NULL结果；将其扩展为
					 * 一行全为NULL的行。我们依赖expectedDesc来
					 * 形成这样的行。 (注意：如果tuplestore_putvalues
					 * 保存了提供描述符中的tdtypeid/tdtypmod，那将会是
					 * 问题，因为这可能与我们从函数本身得到的内容不匹配。
					 * 但它并没有。)
					 */
					int			fc_natts = fc_expectedDesc->natts;
					bool	   *fc_nullflags;

					fc_nullflags = (bool *) palloc(fc_natts * sizeof(bool));
					memset(fc_nullflags, true, fc_natts * sizeof(bool));
					tuplestore_putvalues(fc_tupstore, fc_expectedDesc, NULL, fc_nullflags);
				}
			}
			else
			{
				/* 标量类型情况：只需存储函数结果 */
				tuplestore_putvalues(fc_tupstore, fc_tupdesc, &fc_result, &fcinfo->isnull);
			}

			/*
			 * 完成了吗？
			 */
			if (fc_rsinfo.isDone != ExprMultipleResult)
				break;

			/*
			 * 检查集合返回函数是否被正确声明。
			 * (注意：出于历史原因，如果非SRF返回ExprEndResult，
			 * 我们不会抱怨；这被视为返回NULL。)
			 */
			if (!fc_returnsSet)
				ereport(ERROR,
						(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
						 errmsg("table-function protocol for value-per-call mode was not followed")));
		}
		else if (fc_rsinfo.returnMode == SFRM_Materialize)
		{
			/* 检查我们与函数作者是否在同一页 */
			if (!fc_first_time || fc_rsinfo.isDone != ExprSingleResult || !fc_returnsSet)
				ereport(ERROR,
						(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
						 errmsg("table-function protocol for materialize mode was not followed")));
			/* 完成集合结果的评估 */
			break;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
					 errmsg("unrecognized table-function returnMode: %d",
							(int) fc_rsinfo.returnMode)));

		fc_first_time = false;
	}

no_function_result:

	/*
	 * 如果我们从函数得到的结果是空（即，空集合或NULL结果），
	 * 我们必须创建tuplestore以返回，如果它是一个
	 * 非集合返回函数，则插入一行全为NULL的行。 与上面一样，
	 * 我们依赖expectedDesc来制造虚拟行。
	 */
	if (fc_rsinfo.setResult == NULL)
	{
		MemoryContext fc_oldcontext =
		MemoryContextSwitchTo(fc_econtext->ecxt_per_query_memory);

		fc_tupstore = tuplestore_begin_heap(fc_randomAccess, false, work_mem);
		fc_rsinfo.setResult = fc_tupstore;
		MemoryContextSwitchTo(fc_oldcontext);

		if (!fc_returnsSet)
		{
			int			fc_natts = fc_expectedDesc->natts;
			bool	   *fc_nullflags;

			fc_nullflags = (bool *) palloc(fc_natts * sizeof(bool));
			memset(fc_nullflags, true, fc_natts * sizeof(bool));
			tuplestore_putvalues(fc_tupstore, fc_expectedDesc, NULL, fc_nullflags);
		}
	}

	/*
	 * 如果函数提供了tupdesc，进行交叉检查。我们其实只需要
	 * 对返回RECORD的函数这样做，但最好还是都做。
	 */
	if (fc_rsinfo.setDesc)
	{
		fc_tupledesc_match(fc_expectedDesc, fc_rsinfo.setDesc);

		/*
		 * 如果它是动态分配的TupleDesc，释放它：它通常是在
		 * 每个查询上下文中分配的，因此我们必须避免
		 * 在多个使用间泄漏。
		 */
		if (fc_rsinfo.setDesc->tdrefcount == -1)
			FreeTupleDesc(fc_rsinfo.setDesc);
	}

	MemoryContextSwitchTo(fc_callerContext);

	/* 全部完成，返回tuplestore */
	return fc_rsinfo.setResult;
}


/*
 * 准备目标列表SRF函数调用以进行执行。
 *
 * 这由nodeProjectSet.c使用。
 */
SetExprState *
ExecInitFunctionResultSet(Expr *fc_expr,
						  ExprContext *fc_econtext, PlanState *fc_parent)
{
	SetExprState *fc_state = makeNode(SetExprState);

	fc_state->funcReturnsSet = true;
	fc_state->expr = fc_expr;
	fc_state->func.fn_oid = InvalidOid;

	/*
	 * 初始化元数据。表达式节点可以是FuncExpr或
	 * OpExpr。
	 */
	if (IsA(fc_expr, FuncExpr))
	{
		FuncExpr   *fc_func = (FuncExpr *) fc_expr;

		fc_state->args = ExecInitExprList(fc_func->args, fc_parent);
		fc_init_sexpr(fc_func->funcid, fc_func->inputcollid, fc_expr, fc_state, fc_parent,
				   fc_econtext->ecxt_per_query_memory, true, true);
	}
	else if (IsA(fc_expr, OpExpr))
	{
		OpExpr	   *fc_op = (OpExpr *) fc_expr;

		fc_state->args = ExecInitExprList(fc_op->args, fc_parent);
		fc_init_sexpr(fc_op->opfuncid, fc_op->inputcollid, fc_expr, fc_state, fc_parent,
				   fc_econtext->ecxt_per_query_memory, true, true);
	}
	else
		elog(ERROR, "unrecognized node type: %d",
			 (int) nodeTag(fc_expr));

	/* 除非所选函数返回集合，否则不应该在这里 */
	Assert(fc_state->func.fn_retset);

	return fc_state;
}

/*
 *		ExecMakeFunctionResultSet
 *
 * 评估集合返回函数的参数，然后调用
 * 函数本身。参数表达式可能不包含集合返回的
 * 函数（规划器应该已经为这些函数分开评估）。
 *
 * 这应该在短暂的（每个元组）上下文中调用，
 * argContext需要一直生存到所有行被返回（即*isDone设置为
 * ExprEndResult或ExprSingleResult）。
 *
 * 这由nodeProjectSet.c使用。
 */
Datum ExecMakeFunctionResultSet(SetExprState *fc_fcache,
						  ExprContext *fc_econtext,
						  MemoryContext fc_argContext,
						  bool *fc_isNull,
						  ExprDoneCond *fc_isDone)
{
	List	   *fc_arguments;
	Datum		fc_result;
	FunctionCallInfo fcinfo;
	PgStat_FunctionCallUsage fc_fcusage;
	ReturnSetInfo fc_rsinfo;
	bool		fc_callit;
	int			fc_i;

restart:

	/* 防止因过于复杂的表达式导致栈溢出 */
	check_stack_depth();

	/*
	 * 如果前一次调用函数返回的是元组存储形式的集合结果，
	 * 则继续从元组存储中读取行，直到为空。
	 */
	if (fc_fcache->funcResultStore)
	{
		TupleTableSlot *fc_slot = fc_fcache->funcResultSlot;
		MemoryContext fc_oldContext;
		bool		fc_foundTup;

		/*
		 * 必须确保插槽中的元组生存足够长，否则
		 * 清除插槽可能会尝试释放已经释放的内容。
		 */
		fc_oldContext = MemoryContextSwitchTo(fc_slot->tts_mcxt);
		fc_foundTup = tuplestore_gettupleslot(fc_fcache->funcResultStore, true, false,
										   fc_fcache->funcResultSlot);
		MemoryContextSwitchTo(fc_oldContext);

		if (fc_foundTup)
		{
			*fc_isDone = ExprMultipleResult;
			if (fc_fcache->funcReturnsTuple)
			{
				/* 我们必须将整个元组作为Datum返回。 */
				*fc_isNull = false;
				return ExecFetchSlotHeapTupleDatum(fc_fcache->funcResultSlot);
			}
			else
			{
				/* 提取第一列并将其作为标量返回。 */
				return slot_getattr(fc_fcache->funcResultSlot, 1, fc_isNull);
			}
		}
		/* 用尽了tuplestore，因此进行清理 */
		tuplestore_end(fc_fcache->funcResultStore);
		fc_fcache->funcResultStore = NULL;
		*fc_isDone = ExprEndResult;
		*fc_isNull = true;
		return (Datum) 0;
	}

	/*
	 * arguments是要评估的表达式列表，然后传递给
	 * 函数管理器。我们跳过评估，如果在
	 * 先前的调用中已经完成（即，我们正在继续
	 * 评估一个集合值函数）。否则，收集当前参数值到fcinfo。
	 *
	 * 参数必须在一个上下文中生存，至少要持续到从这个SRF返回所有
	 * 行，否则ValuePerCall SRFs将在第一行返回后引用释放的内存。
	 */
	fcinfo = fc_fcache->fcinfo;
	fc_arguments = fc_fcache->args;
	if (!fc_fcache->setArgsValid)
	{
		MemoryContext fc_oldContext = MemoryContextSwitchTo(fc_argContext);

		fc_ExecEvalFuncArgs(fcinfo, fc_arguments, fc_econtext);
		MemoryContextSwitchTo(fc_oldContext);
	}
	else
	{
		/* 重置标志（我们可能会在下面再次设置它） */
		fc_fcache->setArgsValid = false;
	}

	/*
	 * 现在调用函数，传递评估后的参数值。
	 */

	/* 准备一个resultinfo节点以进行通信。 */
	fcinfo->resultinfo = (Node *) &fc_rsinfo;
	fc_rsinfo.type = T_ReturnSetInfo;
	fc_rsinfo.econtext = fc_econtext;
	fc_rsinfo.expectedDesc = fc_fcache->funcResultDesc;
	fc_rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
	/* 注意我们不设置SFRM_Materialize_Random或_Preferred */
	fc_rsinfo.returnMode = SFRM_ValuePerCall;
	/* isDone 在下面填充 */
	fc_rsinfo.setResult = NULL;
	fc_rsinfo.setDesc = NULL;

	/*
	 * 如果函数是严格的，并且有任何NULL参数，则跳过调用
	 * 函数。
	 */
	fc_callit = true;
	if (fc_fcache->func.fn_strict)
	{
		for (fc_i = 0; fc_i < fcinfo->nargs; fc_i++)
		{
			if (fcinfo->args[fc_i].isnull)
			{
				fc_callit = false;
				break;
			}
		}
	}

	if (fc_callit)
	{
		pgstat_init_function_usage(fcinfo, &fc_fcusage);

		fcinfo->isnull = false;
		fc_rsinfo.isDone = ExprSingleResult;
		fc_result = FunctionCallInvoke(fcinfo);
		*fc_isNull = fcinfo->isnull;
		*fc_isDone = fc_rsinfo.isDone;

		pgstat_end_function_usage(&fc_fcusage,
								  fc_rsinfo.isDone != ExprMultipleResult);
	}
	else
	{
		/* 对于严格的SRF，NULL的结果是一个空集合 */
		fc_result = (Datum) 0;
		*fc_isNull = true;
		*fc_isDone = ExprEndResult;
	}

	/* 函数想要使用哪个协议？ */
	if (fc_rsinfo.returnMode == SFRM_ValuePerCall)
	{
		if (*fc_isDone != ExprEndResult)
		{
			/*
			 * 保存当前参数值以便在下一个调用中重用。
			 */
			if (*fc_isDone == ExprMultipleResult)
			{
				fc_fcache->setArgsValid = true;
				/* 注册清理回调（如果我们还没有） */
				if (!fc_fcache->shutdown_reg)
				{
					RegisterExprContextCallback(fc_econtext,
												fc_ShutdownSetExpr,
												PointerGetDatum(fc_fcache));
					fc_fcache->shutdown_reg = true;
				}
			}
		}
	}
	else if (fc_rsinfo.returnMode == SFRM_Materialize)
	{
		/* 检查我们与函数作者是否在同一页 */
		if (fc_rsinfo.isDone != ExprSingleResult)
			ereport(ERROR,
					(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
					 errmsg("table-function protocol for materialize mode was not followed")));
		if (fc_rsinfo.setResult != NULL)
		{
			/* 准备从元组存储返回值 */
			fc_ExecPrepareTuplestoreResult(fc_fcache, fc_econtext,
										fc_rsinfo.setResult,
										fc_rsinfo.setDesc);
			/* 循环返回顶部以开始从 tuplestore 返回 */
			goto restart;
		}
		/* 如果 setResult 为空，则视为空集合 */
		*fc_isDone = ExprEndResult;
		*fc_isNull = true;
		fc_result = (Datum) 0;
	}
	else
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
				 errmsg("unrecognized table-function returnMode: %d",
						(int) fc_rsinfo.returnMode)));

	return fc_result;
}


/*
 * init_sexpr - 初始化 SetExprState 节点，首次使用时
 */
static void fc_init_sexpr(Oid fc_foid, Oid fc_input_collation, Expr *fc_node,
		   SetExprState *fc_sexpr, PlanState *fc_parent,
		   MemoryContext fc_sexprCxt, bool fc_allowSRF, bool fc_needDescForSRF)
{
	AclResult	fc_aclresult;
	size_t		fc_numargs = list_length(fc_sexpr->args);

	/* 检查调用函数的权限 */
	fc_aclresult = pg_proc_aclcheck(fc_foid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION, get_func_name(fc_foid));
	InvokeFunctionExecuteHook(fc_foid);

	/* 
	 * 对 nargs 的安全检查。在正常情况下，这绝对不应该
	 * 失败，因为解析器应该更早检查。但是，可能它会失败，如果
	 * 服务器已编译的 FUNC_MAX_ARGS 小于 pg_proc 中声明的某些函数？
	 */
	if (list_length(fc_sexpr->args) > FUNC_MAX_ARGS)
		ereport(ERROR,
				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
				 errmsg_plural("cannot pass more than %d argument to a function",
							   "cannot pass more than %d arguments to a function",
							   FUNC_MAX_ARGS,
							   FUNC_MAX_ARGS)));

	/* 设置主fmgr查找信息 */
	fmgr_info_cxt(fc_foid, &(fc_sexpr->func), fc_sexprCxt);
	fmgr_info_set_expr((Node *) fc_sexpr->expr, &(fc_sexpr->func));

	/* 也要初始化函数调用参数结构体 */
	fc_sexpr->fcinfo =
		(FunctionCallInfo) palloc(SizeForFunctionCallInfo(fc_numargs));
	InitFunctionCallInfoData(*fc_sexpr->fcinfo, &(fc_sexpr->func),
							 fc_numargs,
							 fc_input_collation, NULL, NULL);

	/* 如果函数返回集合，检查调用者是否允许 */
	if (fc_sexpr->func.fn_retset && !fc_allowSRF)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set"),
				 fc_parent ? executor_errposition(fc_parent->state,
											   exprLocation((Node *) fc_node)) : 0));

	/* 否则，调用者应该正确标记 sexpr */
	Assert(fc_sexpr->func.fn_retset == fc_sexpr->funcReturnsSet);

	/* 如果函数返回集合，准备预期的元组描述符 */
	if (fc_sexpr->func.fn_retset && fc_needDescForSRF)
	{
		TypeFuncClass fc_functypclass;
		Oid			fc_funcrettype;
		TupleDesc	fc_tupdesc;
		MemoryContext fc_oldcontext;

		fc_functypclass = get_expr_result_type(fc_sexpr->func.fn_expr,
											&fc_funcrettype,
											&fc_tupdesc);

		/* 必须在 sexpr 的上下文中保存 tupdesc */
		fc_oldcontext = MemoryContextSwitchTo(fc_sexprCxt);

		if (fc_functypclass == TYPEFUNC_COMPOSITE ||
			fc_functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
		{
			/* 复合数据类型，例如表的行类型 */
			Assert(fc_tupdesc);
			/* 必须从 typcache 中复制以确保安全 */
			fc_sexpr->funcResultDesc = CreateTupleDescCopy(fc_tupdesc);
			fc_sexpr->funcReturnsTuple = true;
		}
		else if (fc_functypclass == TYPEFUNC_SCALAR)
		{
			/* 基本数据类型，即标量 */
			fc_tupdesc = CreateTemplateTupleDesc(1);
			TupleDescInitEntry(fc_tupdesc,
							   (AttrNumber) 1,
							   NULL,
							   fc_funcrettype,
							   -1,
							   0);
			fc_sexpr->funcResultDesc = fc_tupdesc;
			fc_sexpr->funcReturnsTuple = false;
		}
		else if (fc_functypclass == TYPEFUNC_RECORD)
		{
			/* 如果函数不需要 expectedDesc，这将有效 */
			fc_sexpr->funcResultDesc = NULL;
			fc_sexpr->funcReturnsTuple = true;
		}
		else
		{
			/* 否则，如果函数需要 expectedDesc 将失败 */
			fc_sexpr->funcResultDesc = NULL;
		}

		MemoryContextSwitchTo(fc_oldcontext);
	}
	else
		fc_sexpr->funcResultDesc = NULL;

	/* 初始化额外状态 */
	fc_sexpr->funcResultStore = NULL;
	fc_sexpr->funcResultSlot = NULL;
	fc_sexpr->shutdown_reg = false;
}

/*
 * 回调函数，以防在其运行完成之前需要关闭 SetExprState
 */
static void fc_ShutdownSetExpr(Datum fc_arg)
{
	SetExprState *fc_sexpr = castNode(SetExprState, DatumGetPointer(fc_arg));

	/* 如果我们有一个槽，确保它放弃任何 tuplestore 指针 */
	if (fc_sexpr->funcResultSlot)
		ExecClearTuple(fc_sexpr->funcResultSlot);

	/* 释放任何打开的 tuplestore */
	if (fc_sexpr->funcResultStore)
		tuplestore_end(fc_sexpr->funcResultStore);
	fc_sexpr->funcResultStore = NULL;

	/* 清除任何活动的集合参数状态 */
	fc_sexpr->setArgsValid = false;

	/* execUtils 将注销回调... */
	fc_sexpr->shutdown_reg = false;
}

/*
 * 评估函数的参数。
 */
static void fc_ExecEvalFuncArgs(FunctionCallInfo fcinfo,
				 List *fc_argList,
				 ExprContext *fc_econtext)
{
	int			fc_i;
	ListCell   *fc_arg;

	fc_i = 0;
	foreach(fc_arg, fc_argList)
	{
		ExprState  *fc_argstate = (ExprState *) lfirst(fc_arg);

		fcinfo->args[fc_i].value = ExecEvalExpr(fc_argstate,
											 fc_econtext,
											 &fcinfo->args[fc_i].isnull);
		fc_i++;
	}

	Assert(fc_i == fcinfo->nargs);
}

/*
 * ExecPrepareTuplestoreResult
 *
 * ExecMakeFunctionResultSet 的子例程：准备从 tuplestore 函数结果中提取行。我们必须设置一个 funcResultSlot（除非在前一个调用周期中已经完成）并验证函数返回了预期的元组描述符。
 */
static void fc_ExecPrepareTuplestoreResult(SetExprState *fc_sexpr,
							ExprContext *fc_econtext,
							Tuplestorestate *fc_resultStore,
							TupleDesc fc_resultDesc)
{
	fc_sexpr->funcResultStore = fc_resultStore;

	if (fc_sexpr->funcResultSlot == NULL)
	{
		/* 创建一个槽，以便我们可以从 tuplestore 中读取数据 */
		TupleDesc	fc_slotDesc;
		MemoryContext fc_oldcontext;

		fc_oldcontext = MemoryContextSwitchTo(fc_sexpr->func.fn_mcxt);

		/*
		 * 如果我们无法从上下文中确定结果行类型，并且函数没有返回 tupdesc，我们必须失败。
		 */
		if (fc_sexpr->funcResultDesc)
			fc_slotDesc = fc_sexpr->funcResultDesc;
		else if (fc_resultDesc)
		{
			/* 不要假设 resultDesc 是长期存在的 */
			fc_slotDesc = CreateTupleDescCopy(fc_resultDesc);
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("function returning setof record called in "
							"context that cannot accept type record")));
			fc_slotDesc = NULL;	/* 保持编译器安静 */
		}

		fc_sexpr->funcResultSlot = MakeSingleTupleTableSlot(fc_slotDesc,
														 &TTSOpsMinimalTuple);
		MemoryContextSwitchTo(fc_oldcontext);
	}

	/*
	 * 如果函数提供了tupdesc，进行交叉检查。我们其实只需要
	 * 对返回RECORD的函数这样做，但最好还是都做。
	 */
	if (fc_resultDesc)
	{
		if (fc_sexpr->funcResultDesc)
			fc_tupledesc_match(fc_sexpr->funcResultDesc, fc_resultDesc);

		/*
		 * 如果它是动态分配的TupleDesc，释放它：它通常是在
		 * 每个查询上下文中分配的，因此我们必须避免
		 * 在多个使用间泄漏。
		 */
		if (fc_resultDesc->tdrefcount == -1)
			FreeTupleDesc(fc_resultDesc);
	}

	/* 注册清理回调（如果我们还没有） */
	if (!fc_sexpr->shutdown_reg)
	{
		RegisterExprContextCallback(fc_econtext,
									fc_ShutdownSetExpr,
									PointerGetDatum(fc_sexpr));
		fc_sexpr->shutdown_reg = true;
	}
}

/*
 * 检查函数结果元组类型（src_tupdesc）是否匹配或可以被认为与查询所期望的（dst_tupdesc）匹配。如果它们不匹配，ereport。
 *
 * 我们真正关心的是属性的数量和数据类型。
 * 此外，只要物理存储匹配，我们可以忽略在目标类型中被删除的列的类型不匹配。这在涉及过时的缓存计划的某些情况下是有帮助的。
 */
static void fc_tupledesc_match(TupleDesc fc_dst_tupdesc, TupleDesc fc_src_tupdesc)
{
	int			fc_i;

	if (fc_dst_tupdesc->natts != fc_src_tupdesc->natts)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("function return row and query-specified return row do not match"),
				 errdetail_plural("Returned row contains %d attribute, but query expects %d.",
								  "Returned row contains %d attributes, but query expects %d.",
								  fc_src_tupdesc->natts,
								  fc_src_tupdesc->natts, fc_dst_tupdesc->natts)));

	for (fc_i = 0; fc_i < fc_dst_tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_dattr = TupleDescAttr(fc_dst_tupdesc, fc_i);
		Form_pg_attribute fc_sattr = TupleDescAttr(fc_src_tupdesc, fc_i);

		if (IsBinaryCoercible(fc_sattr->atttypid, fc_dattr->atttypid))
			continue;			/* 不用担心 */
		if (!fc_dattr->attisdropped)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("function return row and query-specified return row do not match"),
					 errdetail("Returned type %s at ordinal position %d, but query expects %s.",
							   format_type_be(fc_sattr->atttypid),
							   fc_i + 1,
							   format_type_be(fc_dattr->atttypid))));

		if (fc_dattr->attlen != fc_sattr->attlen ||
			fc_dattr->attalign != fc_sattr->attalign)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("function return row and query-specified return row do not match"),
					 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
							   fc_i + 1)));
	}
}
